//
//  Collection.swift
//  cnswift-demo
//
//  Created by YangWei on 2020/12/22.
//  Copyright © 2020 YangWei. All rights reserved.
//

import UIKit

/// 集合类型
class CollectionTypes: SWDBaseObject {
    
    class func run() {
        
        let collectionTypes = CollectionTypes()
        
        // 2.2
        collectionTypes.creatingAnEmptyArray()
        
        // 2.3
        collectionTypes.creatingAnArrayWithADefaultValue()
        
        // 2.4
        collectionTypes.creatingAnArrayByAddingTwoArraysTogether()
        
        // 2.5
        collectionTypes.creatingAnArrayWithAnArrayLiteral()
        
        // 2.6
        collectionTypes.accessingAndModifyingAnArray()
        
        // 2.7
        collectionTypes.iteratingOverAnArray()
        
        // 3.3
        collectionTypes.creatingAndInitializingAnEmptySet()
        
        // 3.4
        collectionTypes.creatingASetwithAnArrayLiteral()
        
        // 3.5
        collectionTypes.accessingAndModifyingASet()
        
        // 3.6
        collectionTypes.iteratingOverASet()
        
        // 4.1
        collectionTypes.fundamentalSetOperations()
        
        // 4.2
        collectionTypes.setMembershipAndEquality()
        
        // 5.2
        collectionTypes.creatingAnEmptyDictionary()
        
        // 5.3
        collectionTypes.creatingADictionaryWithADictionaryLiteral()
        
        // 5.4
        collectionTypes.accessingAndModifyingADictionary()
        
        // 5.5
        collectionTypes.iteratingOverADictionary()
    }
    
    /// 2.2 创建一个空数组
    func creatingAnEmptyArray() {
        
        let title = "2.2 创建一个空数组";
        printStartLine(title)
        
        /*
         通过初始化器语法，使用确定类型创建一个空数组：
         */
        var someInts = [Int]()
        print("someInts is of type [Int] with \(someInts.count) items.")
        // prints "someInts is of type [Int] with 0 items."
        
        /*
         使用 [ ] 创建一个空数组
         */
        someInts.append(3)
        // someInts now contains 1 value of type Int
        print("someInts is of type [Int] with \(someInts.count) items.")
         
        someInts = []
        // someInts is now an empty array, but is still of type [Int]
        print("someInts is of type [Int] with \(someInts.count) items.")
        
        printEndLine(title)
    }
    
    /// 2.3 使用默认值创建数组
    func creatingAnArrayWithADefaultValue() {
        
        let title = "2.3 使用默认值创建数组";
        printStartLine(title)
        
        /*
         通过初始化器创建确定大小且元素都设定为相同默认值的数组
         */
        var threeDoubles = Array(repeating: 0.0, count: 3)
        // threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
        print(threeDoubles)
        
        printEndLine(title)
    }
    
    /// 2.4 通过连接两个数组来创建数组
    func creatingAnArrayByAddingTwoArraysTogether() {
        
        let title = "2.4 通过连接两个数组来创建数组";
        printStartLine(title)
        
        /*
         两个相同类型的现存数组用加运算符（ +）加在一起来创建一个新数组
         */
        var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
        // anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]
        print(anotherThreeDoubles)
         
        var threeDoubles = Array(repeating: 0.0, count: 3)
        var sixDoubles = threeDoubles + anotherThreeDoubles
        // sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
        print(sixDoubles)
        
        printEndLine(title)
    }
    
    /// 2.5 使用数组字面量创建数组
    func creatingAnArrayWithAnArrayLiteral() {
        
        let title = "2.5 使用数组字面量创建数组";
        printStartLine(title)
        
        /*
         使用数组字面量来初始化一个数组
         [value 1, value 2, value 3]
         */
        var shoppingList: [String] = ["Eggs", "Milk"]
        
        // shoppingList has been initialized with two initial items
        print(shoppingList)
        
        /*
         如果用包含相同类型值的数组字面量初始化数组，就不需要写明数组的类型。
         shoppingList 的初始化可以写得更短
         */
        var shoppingList2 = ["Eggs", "Milk"]
        print(shoppingList2)
        
        printEndLine(title)
    }
    
    /// 2.6 访问和修改数组
    func accessingAndModifyingAnArray() {
        
        let title = "2.6 访问和修改数组";
        printStartLine(title)
        
        var shoppingList = ["Eggs", "Milk"]
        
        /*
         获取数组中元素的数量：
         */
        print("The shopping list contains \(shoppingList.count) items.")
        // prints "The shopping list contains 2 items."
        
        /*
         使用布尔量 isEmpty 属性来检查 count属性是否等于 0
         */
        if shoppingList.isEmpty {
            print("The shopping list is empty.")
        } else {
            print("The shopping list is not empty.")
        }
        // prints "The shopping list is not empty."
        
        /*
         通过 append(_:)方法给数组末尾添加新的元素：
         */
        shoppingList.append("Flour")
        // shoppingList now contains 3 items, and someone is making pancakes
        print(shoppingList)
        
        /*
         使用 += 来在数组末尾添加一个或者多个同类型元素
         */
        shoppingList += ["Baking Powder"]
        // shoppingList now contains 4 items
        shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
        // shoppingList now contains 7 items
        print(shoppingList)
        
        /* 通过下标语法来从数组当中取回一个值 */
        var firstItem = shoppingList[0]
        // firstItem is equal to "Eggs"
        print(firstItem);
        
        /*
         使用下标来改变给定索引中已经存在的值：
         */
        shoppingList[0] = "Six eggs"
        // the first item in the list is now equal to "Six eggs" rather than "Eggs"
        print(shoppingList)
        
        /*
         使用下标一次改变一个范围的值，就算替换与范围长度不同的值的合集也行。
         用 "Bananas"和 "Apples"替换 "Chocolate Spread", "Cheese", and "Butter"：
         */
        shoppingList[4...6] = ["Bananas", "Apples"]
        // shoppingList now contains 6 items
        print(shoppingList)
        
        /*
         插入元素到特定的索引位置，调用数组的 insert(_:at:)方法：
         */
        shoppingList.insert("Maple Syrup", at: 0)
        // shoppingList now contains 7 items
        // "Maple Syrup" is now the first item in the list
        print(shoppingList)
        
        /*
         使用 remove(at:)方法来移除一个元素。
         这个方法移除特定索引的元素并且返回它（不需要的话可以无视返回的值）
         */
        let mapleSyrup = shoppingList.remove(at: 0)
        // the item that was at index 0 has just been removed
        // shoppingList now contains 6 items, and no Maple Syrup
        // the mapleSyrup constant is now equal to the removed "Maple Syrup" string
        print(shoppingList)
        print(mapleSyrup)
        
        /*
         使用 removeLast()方法移除数组最后一个元素，方法返回删除了的元素
         */
        let apples = shoppingList.removeLast()
        // the last item in the array has just been removed
        // shoppingList now contains 5 items, and no apples
        // the apples constant is now equal to the removed "Apples" string
        print(shoppingList)
        print(apples)
        
        printEndLine(title)
    }
    
    /// 2.7 遍历一个数组
    func iteratingOverAnArray() {
        
        let title = "2.7 遍历一个数组";
        printStartLine(title)
        
        var shoppingList = ["Eggs", "Milk"]
        
        for (index, value) in shoppingList.enumerated() {
            print("Item \(index + 1): \(value)")
        }
        
        printEndLine(title)
    }
    
    /// 3.3 创建并初始化一个空 Set
    func creatingAndInitializingAnEmptySet() {
        
        let title = "3.3 创建并初始化一个空 Set";
        printStartLine(title)
        
        /*
         使用初始化器来创建一个确定类型的空 Set：
         */
        var letters = Set<Character>()
        print("letters is of type Set<Character> with \(letters.count) items.")
        // prints "letters is of type Set<Character> with 0 items."

        letters.insert("a")
        // letters now contains 1 value of type Character
        print(letters)
        letters = []
        // letters is now an empty set, but is still of type Set<Character>
        print(letters)
        
        printEndLine(title)
    }
    
    /// 3.4 使用数组字面量创建 Set
    func creatingASetwithAnArrayLiteral() {
        
        let title = "3.4 使用数组字面量创建 Set";
        printStartLine(title)
        
        /*
         使用数组来初始化一个 Set
         */
        var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
        print(favoriteGenres)

        // 有初始值，可以不用声明 String 类型
        var favoriteGenres2: Set = ["Rock", "Classical", "Hip hop"]
        print(favoriteGenres2)
        
        printEndLine(title)
    }
    
    /// 3.5 访问和修改 Set
    func accessingAndModifyingASet() {
        
        let title = "3.5 访问和修改 Set";
        printStartLine(title)
        
        var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
        
        /*
         count 属性：获取 Set 内元素的数量
         */
        print("I have \(favoriteGenres.count) favorite music genres.")
        
        /*
         isEmpty 属性：检查 count 是否等于 0：
         */
        if favoriteGenres.isEmpty {
            print("As far as music goes, I'm not picky.")
        } else {
            print("I have particular music preferences.")
        }
        
        /*
         insert(_:)方法：添加一个新的元素到 Set：
         */
        favoriteGenres.insert("Jazz")
        print(favoriteGenres)
        
        /*
         remove(_:)方法：从合集当中移除一个元素，并返回移除的值，如果没有就返回 nil。
         */
        if let removedGenre = favoriteGenres.remove("Rock") {
            print("\(removedGenre)? I'm over it.")
        } else {
            print("I never much cared for that.")
        }
        print(favoriteGenres)
        
        /*
         contains(_:)方法：检查 Set 是否包含了特定的元素
         */
        if favoriteGenres.contains("Funk") {
            print("I get up on the good foot.")
        } else {
            print("It's too funky in here.")
        }
        
        /*
         removeAll()方法：一次移除合集当中所有的元素
         */
        favoriteGenres.removeAll()
        print(favoriteGenres)
        
        printEndLine(title)
    }
    
    /// 3.6 遍历合集
    func iteratingOverASet() {
        
        let title = "3.6 遍历合集";
        printStartLine(title)
        
        var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
        
        /*
         for-in 循环里遍历合集的值
         */
        for genre in favoriteGenres {
            print("\(genre)")
        }
        
        /*
         Set 类型是无序的。sorted()方法：把合集的元素作为使用升序返回一个 数组。
         */
        for genre in favoriteGenres.sorted() {
            print("\(genre)")
        }
        
        printEndLine(title)
    }
    
    /// 4.1 基本 Set 操作
    func fundamentalSetOperations() {
        
        let title = "4.1 基本 Set 操作";
        printStartLine(title)
        
        /*
         intersection(_:)方法：取交集创建新 Set；
         symmetricDifference(_:)方法：来创建一个非共有值的新 Set；
         union(_:)方法：取并集创建新的 Set；
         subtracting(_:)方法：做减法得到剩余部分的一个 Set
         */
        let oddDigits: Set = [1, 3, 5, 7, 9]
        print("oddDigits = ", oddDigits)
        
        let evenDigits: Set = [0, 2, 4, 6, 8]
        print("evenDigits = ", evenDigits)
        
        let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
        print("singleDigitPrimeNumbers = ", singleDigitPrimeNumbers)
        
        // 并集
        let unionArray = oddDigits.union(evenDigits).sorted()
        // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        print("oddDigits.union(evenDigits) = ", unionArray)
        
        // 交集
        let intersectionArray = oddDigits.intersection(evenDigits).sorted()
        // []
        print("oddDigits.intersection(evenDigits) = ", intersectionArray)
        
        // 差集，减法
        let subtractingArray = oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
        // [1, 9]
        print("oddDigits.subtracting(singleDigitPrimeNumbers) = ", subtractingArray)
        
        // 非共有集
        let symmetricDifferenceArray = oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
        // [1, 2, 9]
        print("oddDigits.symmetricDifference(singleDigitPrimeNumbers) = ", symmetricDifferenceArray)
        
        printEndLine(title)
    }
    
    /// 4.2 Set 成员关系和相等性
    func setMembershipAndEquality() {
        
        let title = "4.2 Set 成员关系和相等性";
        printStartLine(title)
        
        /*
         - “相等”运算符 ( == )来判断两个 Set 是否包含有相同的值；
         - isSubset(of:) 方法来确定一个 Set 的所有值是被某 Set 包含；
         - isSuperset(of:) 方法来确定一个 Set 是否包含某个 Set 的所有值；
         - isStrictSubset(of:) 或者 isStrictSuperset(of:)方法来确定 Set 是否为某一个合集的子集或者超集，但并不相等；
         - isDisjoint(with:)方法来判断两个 Set 是否拥有完全不同的值。
         */
        
        let houseAnimals: Set = ["🐶", "🐱"]
        let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
        let cityAnimals: Set = ["🐦", "🐭"]
         
        print("houseAnimals: \(houseAnimals)")
        print("farmAnimals: \(farmAnimals)")
        print("cityAnimals: \(cityAnimals)")
        
        print("houseAnimals.isSubset(of: farmAnimals) :", houseAnimals.isSubset(of: farmAnimals))
        // true
        print("farmAnimals.isSuperset(of: houseAnimals) :", farmAnimals.isSuperset(of: houseAnimals))
        // true
        print("farmAnimals.isDisjoint(with: cityAnimals) :", farmAnimals.isDisjoint(with: cityAnimals))
        // true
        
        printEndLine(title)
    }
    
    /// 5.2 创建一个空字典
    func creatingAnEmptyDictionary() {
        
        let title = "5.2 创建一个空字典";
        printStartLine(title)
        
        /*
         用初始化器语法来创建一个空 Dictionary：
         */
        var namesOfIntegers = [Int: String]()

        namesOfIntegers[16] = "sixteen"
        print(namesOfIntegers)
        
        // 置为空
        namesOfIntegers = [:]
        print(namesOfIntegers)
        
        printEndLine(title)
    }
    
    /// 5.3 用字典字面量创建字典
    func creatingADictionaryWithADictionaryLiteral() {
        
        let title = "5.3 用字典字面量创建字典";
        printStartLine(title)
        
        /*
         基本初始格式：
         [key 1: value 1, key 2: value 2, key 3: value 3]
         */
        var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

        print(airports)
        
        // 有初始值，可以简化
        var airports2 = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
        
        printEndLine(title)
    }
    
    /// 5.4 访问和修改字典
    func accessingAndModifyingADictionary() {
        
        let title = "5.4 访问和修改字典";
        printStartLine(title)
        
        var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
        
        /*
         使用 count 只读属性查看 Dictionary拥有多少元素
         */
        print("The airports dictionary contains \(airports.count) items.")
        
        /*
         isEmpty 属性检查 count属性是否等于 0
         */
        if airports.isEmpty {
            print("The airports dictionary is empty.")
        } else {
            print("The airports dictionary is not empty.")
        }
        
        /*
         用下标脚本给字典添加新元素，修改元素
         */
        airports["LHR"] = "London"
        print(airports)

        airports["LHR"] = "London Heathrow"
        print(airports)
        
        /*
         使用 updateValue(_:forKey:)方法来设置或者更新特定键的值。
         updateValue(_:forKey:)方法会在键没有值的时候设置一个值，或者在键已经存在的时候更新它。
         不同于下标方式， updateValue(_:forKey:)方法在执行更新之后返回旧的值。
         可以用来检查更新是否成功。
         updateValue(_:forKey:)方法返回的是一个字典值类型的可选项值。
         比如对于储存 String 值的字典来说，方法会返回 String?类型的值，或者说“可选的 String”。
         如果更新前存在的话，这个可选项包含了键的旧值，否则就是 nil：
         */
        if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
            print("The old value for DUB was \(oldValue).")
        }
        
        /*
         使用下标语法来从字典中取回值，可能请求的键没有值，字典的下标返回可选的字典值类型。
         如果字典包含了请求的键的值，下标脚本就返回一个包含这个键的值的可选项。否则，下标脚本返回 nil
         */
        if let airportName = airports["DUB"] {
            print("The name of the airport is \(airportName).")
        } else {
            print("That airport is not in the airports dictionary.")
        }
        
        /*
         使用下标脚本给一个键赋值 nil 来从字典当中移除一个键值对：
         */
        airports["APL"] = "Apple International"
        print("airports = ", airports)
        airports["APL"] = nil
        print("airports = ", airports)
        
        /*
         使用 removeValue(forKey:) 来从字典里移除键值对。
         移除键值对如存在，返回移除的值，如果值不存在则返回 nil：
         */
        if let removedValue = airports.removeValue(forKey: "DUB") {
            print("The removed airport's name is \(removedValue).")
        } else {
            print("The airports dictionary does not contain a value for DUB.")
        }
        
        printEndLine(title)
    }
    
    /// 5.5 遍历字典
    func iteratingOverADictionary() {
        
        let title = "5.5 遍历字典";
        printStartLine(title)
        
        var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
        
        for (airportCode, airportName) in airports {
            print("\(airportCode): \(airportName)")
        }
        
        /*
         访问字典的 keys 和 values 属性来取回可遍历的字典的键或值的集合
         */
        for airportCode in airports.keys {
            print("Airport code: \(airportCode)")
        }
         
        for airportName in airports.values {
            print("Airport name: \(airportName)")
        }
        
        printEndLine(title)
    }
}
